﻿/**
* @description  Abstract base class for managing the loading and playback of different types of media.
*/
package com.moke.media.controllers {

	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.events.Event;
	import flash.utils.Timer;
	import flash.media.SoundTransform;

	import com.moke.media.events.MediaEvent;
	import com.moke.media.IAudible;
	import com.moke.media.IPlayable;
	
	public class MediaController extends EventDispatcher implements IAudible, IPlayable {
	
		private var _mediaFile:String;
		private var _mediaDuration:Number;
		private var _playing:Boolean;
		private var _paused:Boolean;
		private var _volume:Number;
		private var _pan:Number;
		private var _progressTimer:Timer;

		static private var _progressInterval:Number = 40;
		
		/**
		* @description  Constructor for Abstract class. Only concrete classes should call this.
		*/		
		public function MediaController() {
			init();
		}
	
		/**
		* @description  Initializes class variables.
		*/		
		protected function init():void {
			_volume = 1;
			_pan = 0;
			_paused = false;
			_playing = false;
			_progressTimer = new Timer(_progressInterval, 0);
		}
	
		/**
		* @description  Starts or stops timer that monitors media playback.
		*
		* @param  pStart  True to start monitoring. False otherwise.
		*/		
		protected function startTrackProgress(pStart:Boolean):void {
			if (pStart) {
				dispatchEvent(new MediaEvent(MediaEvent.START, mediaPosition, mediaDuration));
				_progressTimer.addEventListener(TimerEvent.TIMER, trackProgress, false, 0, true);
				_progressTimer.start();
			} else {
				dispatchEvent(new MediaEvent(MediaEvent.STOP, mediaPosition, mediaDuration));
				_progressTimer.removeEventListener(TimerEvent.TIMER, trackProgress);
				_progressTimer.stop();
			}
		}
	
		/**
		* @description  Called at an interval as the media is played back.
		*
		* @param  pEvent  The event fired by the Timer.
		*/		
		protected function trackProgress(pEvent:Event):void {
			dispatchEvent(new MediaEvent(MediaEvent.PROGRESS, mediaPosition, mediaDuration));
		}
		
		/**
		* @description  Applies current sound settings to media. To be overridden.
		*/		
		protected function applySoundTransform():void {}
	
		/**
		* @description  Triggers the request and loading of the controller's media.
		*
		* @param  pFileURL  The http or rtmp path to the file to load.
		*/		
		public function loadMedia(pFileURL:String):void {
			_mediaFile = pFileURL;
			_playing = false;
			_paused = false;
		}
	
		/**
		* @description  Begins playback of the loaded media.
		*/		
		public function startMedia():void {
			_playing = true;
			setVolume(_volume);
			setPan(_pan);
			startTrackProgress(true);
		} 
	
		/**
		* @description  Sends the media to the specified position, in milliseconds.
		*
		* @param  pOffset  The position in milliseconds to send the media to.
		*/
		public function seek(pOffset:Number):void {
			dispatchEvent(new MediaEvent(MediaEvent.PROGRESS, mediaPosition, mediaDuration));
		}
	
		/**
		* @description  Halts playback of media.
		*/		
		public function stopMedia():void {
			_playing = false;
			startTrackProgress(false);
		} 
	
		/**
		* @description  Pauses or resumes playback of media.
		*
		* @param  pPause  True to pause media. False to resume.
		*/		
		public function pauseMedia(pPause:Boolean):void {
			_playing = !pPause;
			_paused = pPause;
			startTrackProgress(!pPause);
		} 
		
		public function die():void {
			stopMedia();
		}
	
		/**
		* @description  Returns the path to the loaded media.
		*
		* @returns  The http or rtmp path to the media loaded.
		*/		
		public function get mediaFile():String { return _mediaFile; }
		
		/**
		* @description  Returns the current position, in milliseconds, of the media.
		*
		* @returns  The current position, in milliseconds, of the media.
		*/		
		public function get mediaPosition():Number { return 0; }

		/**
		* @description  Sets the current position, in milliseconds, of the media.
		*
		* @param  pPosition  The position, in milliseconds, to send the media to.
		*/		
		public function set mediaPosition(pPosition:Number):void {}

		/**
		* @description  Returns the duration, in milliseconds, of the loaded media.
		*
		* @returns  The duration, in milliseconds, of the loaded media.
		*/		
		public function get mediaDuration():Number { return _mediaDuration; }

		/**
		* @description  Sets the known duration, in milliseconds, of the loaded media.
		*
		* @param  pDuration  The duration, in milliseconds, of the loaded media.
		*/
		public function set mediaDuration(pDuration:Number):void { _mediaDuration = pDuration; }

		/**
		* @description  Returns whether the media in the controller is currently paused.
		*
		* @returns  True is the media is paused.
		*/		
		public function get paused():Boolean { return _paused; }
	
		/**
		* @description  Returns whether the media in the controller is currently playing.
		*
		* @returns  True is the media is playing.
		*/		
		public function get playing():Boolean { return _playing; }
	
		/**
		* @description  Returns a SoundTransform instance containing the current sound properties for the media.
		*
		* @returns  A new SoundTransform instance containing the volume and pan values for the media.
		*/		
		public function getSoundTransform():SoundTransform {
			return new SoundTransform(_volume, _pan);
		} 

		/**
		* @description  Sets the sound volume and pan properties for the media.
		*
		* @param  pTransform  A SoundTransform instance with volume and pan values to apply to the media.
		*/		
		public function setSoundTransform(pSoundTransform:SoundTransform):void {
			_volume = pSoundTransform.volume;
			_pan = pSoundTransform.pan;
			applySoundTransform();
		}

		/**
		* @description  Returns the current volume of the controller's media.
		*
		* @returns  A number between 0 and 1 representing the volume level of the media.
		*/		
		public function getVolume():Number { return _volume; } 

		/**
		* @description  Sets the volume of the controller's media.
		*
		* @param  pVolume  A number between 0 and 1 representing the volume level of the media.
		*/		
		public function setVolume(pVolume:Number):void {
			_volume = pVolume;
			applySoundTransform();
		}  

		/**
		* @description  Returns the current volume of the controller's media.
		*
		* @returns  A number between 0 and 1 representing the volume level of the media.
		*/		
		public function get volume():Number { return getVolume(); } 

		/**
		* @description  Sets the volume of the controller's media.
		*
		* @param  pVolume  A number between 0 and 1 representing the volume level of the media.
		*/		
		public function set volume(pVolume:Number):void { setVolume(pVolume); } 
	
		/**
		* @description  Returns the current sound pan of the controller's media.
		*
		* @returns  A number between -1 and 1 representing the pan of the media's sound.
		*/		
		public function getPan():Number { return _pan; }

		/**
		* @description  Sets the pan of the controller's media's sound.
		*
		* @param  pPan  A number between -1 and 1 representing the pan of the media's sound.
		*/		
		public function setPan(pPan:Number):void {
			_pan = pPan;
			applySoundTransform();
		} 

		/**
		* @description  Returns the current sound pan of the controller's media.
		*
		* @returns  A number between -1 and 1 representing the pan of the media's sound.
		*/		
		public function get pan():Number { return getPan(); } 

		/**
		* @description  Sets the pan of the controller's media's sound.
		*
		* @param  pPan  A number between -1 and 1 representing the pan of the media's sound.
		*/		
		public function set pan(pPan:Number):void { setPan(pPan); } 
	
		/**
		* @description  Returns the media playback object for the controller
		*				(Video for FLVs, Sprite for SWFs, Sound for MP3s).
		*
		* @returns  A reference to the object containing the media.
		*/		
		public function get media():Object { return null; }

	}
	
}